package cn.aixuegao.mybatis.processor.impl;

import cn.aixuegao.common.bean.Permission;
import cn.aixuegao.common.bean.Rule;
import cn.aixuegao.common.enums.OperateEnum;
import cn.aixuegao.common.utils.Validator;
import cn.aixuegao.mybatis.factory.ExpressionFactory;
import cn.aixuegao.mybatis.processor.IRestructureProcessor;
import net.sf.jsqlparser.expression.Expression;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.StringValue;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.EqualsTo;
import net.sf.jsqlparser.expression.operators.relational.LikeExpression;
import net.sf.jsqlparser.parser.CCJSqlParserUtil;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.schema.Table;
import net.sf.jsqlparser.statement.select.Join;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import net.sf.jsqlparser.util.TablesNamesFinder;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author: huangxingyao
 * @date: 2020/03/30
 * @what:
 * @why:
 * @how:
 */
public class DefaultRestructureProcessor implements IRestructureProcessor {

    @Override
    public BoundSql process(MappedStatement mappedStatement, BoundSql boundSql, Permission permission) {
        try {

            if (Validator.isNullOrEmpty(permission) || Validator.isNullOrEmpty(permission.getRules())){
                return boundSql;
            }
            //原始语句
            Select originSelect = (Select) CCJSqlParserUtil.parse(boundSql.getSql());

            PlainSelect originPlainSelect = (PlainSelect) originSelect.getSelectBody();

            handleTable(originPlainSelect, permission.getRules());


            BoundSql newSql = new BoundSql(mappedStatement.getConfiguration(),
                                           originPlainSelect.toString(),
                                           boundSql.getParameterMappings(),
                                           boundSql.getParameterObject()
            );

            return newSql;
        } catch (Exception e) {
            throw new RuntimeException(e.getMessage(), e);
        }
    }


    private void handleTable(PlainSelect originPlainSelect, List<Rule> rules) {
        //解析表与别名
        Map<String, String> tableName2alias = parseTables(originPlainSelect);

        Map<String, List<Rule>> table2column = rules.stream().collect(Collectors.groupingBy(Rule::getTableName));

        Iterator<Map.Entry<String, List<Rule>>> iterator = table2column.entrySet().iterator();
        while (iterator.hasNext()) {
            String table = iterator.next().getKey();
            if (Validator.isNullOrEmpty(tableName2alias.get(table))) {
                continue;
            }
            handleColumn(originPlainSelect, tableName2alias, iterator.next().getValue());
        }
    }

    /**
     * 解析表名称与别名
     * @param plainSelect
     * @return
     */
    private Map<String, String> parseTables(PlainSelect plainSelect) {
        Map<String,String> tableName2alias = new HashMap<>();
        Table table = (Table)plainSelect.getFromItem();
        tableName2alias.put(table.getName(),
                            Validator.isNullOrEmpty(table.getAlias()) ? table.getName() : table.getAlias().getName());

        for(Join join : plainSelect.getJoins()){
            Table joinTable = (Table)join.getRightItem();
            tableName2alias.put(joinTable.getName(),
                    Validator.isNullOrEmpty(joinTable.getAlias()) ? joinTable.getName() : joinTable.getAlias().getName());
        }
        return tableName2alias;
    }

    private void handleColumn(PlainSelect resultPlainSelect, Map<String, String> tableName2alias, List<Rule> rules) {

        Iterator<Rule> iterator = rules.iterator();
        while (iterator.hasNext()) {
            Rule next = iterator.next();

            Column column = new Column();
            column.setColumnName(next.getColumn());
            column.setTable(new Table(tableName2alias.get(next.getTableName())));

            Expression perfect = ExpressionFactory.perfect(next.getOperate(),column,next.getValue());

            Expression expression = resultPlainSelect.getWhere();
            if (Validator.isNullOrEmpty(expression)) {
                resultPlainSelect.setWhere(perfect);
            }else {
                Parenthesis parenthesis = new Parenthesis(expression);
                AndExpression andExpression = new AndExpression(parenthesis, perfect);
                resultPlainSelect.setWhere(andExpression);
            }
        }

    }


    public static void main(String[] args) throws Exception{
        DefaultRestructureProcessor defaultRestructureProcessor = new DefaultRestructureProcessor();
        String sql = "select device.device_id,\n" +
                "       device.device_name,\n" +
                "       device.sort_id,\n" +
                "       device.days,\n" +
                "       sort.sort_name,\n" +
                "       case\n" +
                "         when TO_DAYS(max(log.create_time)) - TO_DAYS(device.last_time) <= device.days then 1\n" +
                "         else 0\n" +
                "         end as state\n" +
                "from ops_device as device\n" +
                "       join ops_project as project\n" +
                "            on device.project_id = project.project_id\n" +
                "       join ops_sort as sort\n" +
                "            on sort.id = device.sort_id\n" +
                "       left join ops_patrol_log as log\n" +
                "                 on log.device_id = device.device_id\n" +
                "where device.project_id = 'Pro_001'\n" +
                "group by device.device_id, device.last_time";
        //原始语句
        Select originSelect = (Select) CCJSqlParserUtil.parse(sql);

        PlainSelect plainSelect = (PlainSelect)originSelect.getSelectBody();

        Map<String, String> map = defaultRestructureProcessor.parseTables(plainSelect);

        System.out.println(map);

        Column column = new Column();
        column.setColumnName("a");
        column.setTable(new Table("test"));

        EqualsTo equalsTo = new EqualsTo();
        equalsTo.setLeftExpression(column);
        equalsTo.setRightExpression(new StringValue("789"));

        LikeExpression likeExpression = new LikeExpression();
        likeExpression.setLeftExpression(column);
        likeExpression.setRightExpression(new StringValue("159"));
        likeExpression.setRightExpression(new StringValue("159"));



        System.out.println(equalsTo);
        System.out.println(likeExpression);
    }



}
